package org.journey.datasouece;

import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class MultiTenantDataSource extends AbstractRoutingDataSource {

    private ConcurrentHashMap<Object, Object> targetDataSources = new ConcurrentHashMap<>();

    private String baseUrl = "";
    private String baseDriver = "";
    private String baseUserName = "";
    private String basePassword = "";

    public MultiTenantDataSource(String baseUrl, String baseDriver, String baseUserName, String basePassword) {
        this.baseUrl = baseUrl;
        this.baseDriver = baseDriver;
        this.baseUserName = baseUserName;
        this.basePassword = basePassword;
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        targetDataSources.forEach((key, value) -> this.targetDataSources.put(key, value));
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        log.debug("route to dataSource: {}", DataSourceHolder.getDataSourceName());
        this.verifyAndInitDataSource(DataSourceHolder.getDataSourceName());
        return DataSourceHolder.getDataSourceName();
    }

    private void verifyAndInitDataSource(String dataSourceName) {
        Object obj = this.targetDataSources.get(dataSourceName);
        if(!Optional.ofNullable(obj).isPresent()){
            synchronized (dataSourceName){
                if(!Optional.ofNullable(obj).isPresent()){
                    log.debug("init dataSource: {}", DataSourceHolder.getDataSourceName());
                    HikariDataSource dataSource = DataSourceBuilder.create()
                            .type(HikariDataSource.class)
                            .driverClassName(baseDriver).url(baseUrl.replace(DataSourceConstants.DATA_SOURCE_NAME_REG, dataSourceName))
                            .username(baseUserName).password(basePassword).build();
                    this.addTargetDataSource(dataSourceName, dataSource);
                }
            }
        }
    }

    private void addTargetDataSource(String key, HikariDataSource dataSource) {
        this.targetDataSources.put(key, dataSource);
        super.setTargetDataSources(this.targetDataSources);
        super.afterPropertiesSet();
    }

    public void removeTargetDataSource(String key) {
        log.debug("remove dataSource:"+ key);
        this.targetDataSources.remove(key);
        super.setTargetDataSources(this.targetDataSources);
        super.afterPropertiesSet();
    }
}
